package cn.lcsjsxy.yxc.reflect;

import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 一、类的加载与ClassLoader的理解
 *
 * 》加载：将Class文件字节码内容加载到内存中，并将这些静态数据转换成方法区的运行时数据结构，然后生成一个代表这个类的java.lang.Class对象
 *  作为方法区中类数据的访问入口（即引用地址）。所有需要访问和使用类数据都只能通过这个Class对象。这个加载的过程需要类加载器参与
 *
 * 》链接：将Java类的二进制代码合并到JVM的运行状态之中的过程
 *      》验证：确保加载的类信息符合JVM规范，例如：以cafe开头，没有安全方面的问题
 *      》准备：正式为类变量（static）分配内存并设置类变量默认初始值的阶段，这些内存都将在方法区中进行分配
 *      》解析：虚拟机常量池内的符号引用（常量名）替换为直接引用（地址）的过程
 *
 * 》初始化：
 *      》执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
 *      （类构造器是构造器类信息的，不是构造该类对象的构造器）。
 *      》当初始化一个类的时候，如果发现其父类还没有进行初始化，则需要先触发其父类的初始化。
 *      》虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
 *
 * 》类加载器的作用：
 *      》类加载的作用：将class文件字节码内容加载到内存中，并将这些静态数据转换为方法区的运行时数据结构，然后在堆中生成一个代表这个类的java.lang.Class
 *      对象，作为方法区中类数据的访问入口。
 *      》类缓存：标准的JavaSE类加载器可以按要求查找类，但一旦某个类被加载到类加载器中，它将维持加载（缓存）一段时间。不过JVM垃圾回收机制可以回收这些Class
 *      对象
 */
public class ClassLoaderComprehend {
    @Test
    public void test1(){
        //对于自定义类，使用系统类加载器进行加载
        ClassLoader classLoader=ClassLoaderComprehend.class.getClassLoader();
        System.out.println(classLoader);  //系统类加载器
        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader classLoader1=classLoader.getParent();
        System.out.println(classLoader1);  //扩展类加载器
        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库，无法加载自定义类的。
        ClassLoader classLoader2=classLoader1.getParent();
        System.out.println(classLoader2);  //引导类加载器 null

        ClassLoader classLoader3=String.class.getClassLoader();
        System.out.println(classLoader3);
    }

    @Test
    public void test2() throws IOException {
        Properties pros=new Properties();
        //此时的文件默认在当前的module下
        //读取配置文件的方式一：
//        FileInputStream fis=new FileInputStream("jdbc.properties");
//        pros.load(fis);

        ClassLoader classLoader=ClassLoaderComprehend.class.getClassLoader();
        InputStream is=classLoader.getResourceAsStream("jdbc.properties");
        pros.load(is);

        //读取配置文件的方式二：使用ClassLoader
        //配置文件默认识别为：当前module的src下
        String user=pros.getProperty("user");
        String password=pros.getProperty("password");
        System.out.println(user+"\n"+password);

        is.close();
    }
}
